Esta práctica cubre de forma transversal la asignatura.
Las Prácticas 1 y 2 de la asignatura se plantean de una forma conjunta de modo que la Práctica 2 será continuación de la 1.
El objetivo global de las dos prácticas consiste en seleccionar uno o varios juegos de datos, realizar las tareas de preparación y análisis exploratorio con el objetivo de disponer de datos listos para aplicar algoritmos de clustering, asociación y clasificación.
Las competencias que se trabajan en esta prueba son:
La correcta asimilación de todos los aspectos trabajados durante el semestre.
En esta práctica abordamos un caso real de minería de datos donde tenemos que poner en juego todos los conceptos trabajados. Hay que trabajar todo el ciclo de vida del proyecto. Desde el objetivo del proyecto hasta la implementación del conocimiento encontrado pasando por la preparación, limpieza de los datos, conocimiento de los datos, generación del modelo, interpretación y evaluación.
Material docente proporcionado por la UOC.
Ejercicios prácticos
Para todas las PRA es necesario documentar en cada apartado del ejercicio práctico que se ha hecho y como se ha hecho.
El formato de entrega es: usernameestudiant-PRAn.html/doc/docx/odt/pdf
Fecha de entrega: 02/12/2020
Se debe entregar la PRA_1 en el buzón de entregas del aula
A menudo es inevitable, al producir una obra multimedia, hacer uso de recursos creados por terceras personas. Es por lo tanto comprensible hacerlo en el marco de una práctica de los estudios de Informática, Multimedia y Telecomunicación de la UOC, siempre y cuando esto se documente claramente y no suponga plagio en la práctica.
Por lo tanto, al presentar una práctica que haga uso de recursos ajenos, se debe presentar junto con ella un documento en que se detallen todos ellos, especificando el nombre de cada recurso, su autor, el lugar donde se obtuvo y su estatus legal: si la obra esta protegida por el copyright o se acoge a alguna otra licencia de uso (Creative Commons, licencia GNU, GPL …). El estudiante deberá asegurarse de que la licencia no impide específicamente su uso en el marco de la práctica. En caso de no encontrar la información correspondiente tendrá que asumir que la obra esta protegida por copyright.
Deberéis, además, adjuntar los ficheros originales cuando las obras utilizadas sean digitales, y su código fuente si corresponde.
Todo estudio analítico debe nacer de una necesidad por parte del negocio o de una voluntad de dotarle de un conocimiento contenido en los datos y que solo podremos obtener a través de una colección de buenas prácticas basadas en la Minería de Datos.
El mundo de la analítica de datos se sustenta en 3 ejes:
Uno de ellos es el profundo conocimiento que deberíamos tener del negocio al que tratamos de dar respuestas mediante los estudios analíticos.
El otro gran eje es sin duda las capacidades analíticas que seamos capaces de desplegar y en este sentido, las dos prácticas de esta asignatura pretenden que el estudiante realice un recorrido sólido por este segundo eje.
El tercer eje son los Datos. Las necesidades del Negocio deben concretarse con preguntas analíticas que a su vez sean viables responder a partir de los datos de que disponemos. La tarea de analizar los datos es sin duda importante, pero la tarea de identificarlos y obtenerlos va a ser para un analista un reto permanente.
Como primera parte del estudio analítico que nos disponemos a realizar, se pide al estudiante que complete los siguientes pasos:
Seleccionar un juego de datos y justificar su elección. El juego de datos deberá tener capacidades para que se le puedan aplicar algoritmos supervisados, algoritmos no supervisados y reglas de asociación.
Realizar un análisis exploratorio del juego de datos seleccionado.
Realizar tareas de limpieza y acondicionado para poder ser usado en procesos de modelado.
Realizar métodos de discretización
Aplicar un estudio PCA sobre el juego de datos. A pesar de no estar explicado en el material didáctico, se valorará si en lugar de PCA investigáis por vuestra cuenta y aplicáis SVD (Single Value Decomposition).
En esta práctica se realizará una simulación de un proyecto real de minería de datos. El juego de datos seleccionado será la base para recorrer todo el ciclo de vida del proyecto, pasando por la limpieza y preparación de los datos hasta la obtención del conocimiento, resultado obtenido de la aplicación de los diferentes algoritmos de ML.
En esta primera parte, solamente se realizarán las tares relacionadas con la preparación de los datos, aplicando las diferentes técnicas estudiadas en clase, tales como la normalización, la discretización y el tratamiento de outliers y missing data. De manera que manera los datos quedarán listos para que en la segunda parte de la práctica se le puedan aplicar los diferentes algoritmos. En esta práctica también se aplicará una técnica de reducción de dimensionalidad al dataset SVD (Single Value Decomposition ), esto como resultado de la investigación autodidacta de este tipo de técnicas.
Para elegir el conjunto de datos se ha consultado la plataforma Kaggle, de la que se ha seleccionado el conjunto de datos Pima Indians Diabetes Database.
Este conjunto de datos contiene información relacionada con pacientes del " National Institute of Diabetes and Digestive and Kidney Diseases " de los Estados Unidos, en el se recogen valores de algunas variables médicas con el objetivo de predecir si una persona tiene o no Diabetes, según la descripción del conjunto de datos la mayoría de los pacientes son mujeres mayores de 21 años. A continuación se describen cada una de estas variables predictoras y la variable objetivo.
Este juego de datos ha sido seleccionado después de realizar una larga búsqueda por varios de los repositorios de datos abiertos, su elección está principalmente fundamentada por las capacidades analíticas que se intuyen de los datos y porque cumple a la perfección con los requerimientos indicados en la orientación de la práctica. A este juego de datos se le pueden aplicar algoritmos supervisados, no supervisados y reglas de asociación, y los datos están en un formato que facilitan estas tareas.
Por ejemplo para aplicar algoritmos supervisados se pueden separar los datos en dos partes, las variables predictoras por una lado y la objetivo por otra. Se le pueden aplicar cualquiera de los algoritmos de agrupamiento estudiados en clase.
Con respecto a la clasificación, este dataset también puede ser empleado en la construcción de un modelo válido, solamente sería necesario dividir el conjunto en las partes (después de la limpieza y preparación) de training y testing, y de esta manera poder evaluar evaluar el modelo con los diferentes algoritmos.
Y finalmente, en cuanto a als reglas de asociación, este dataset también puede ser empleado para tareas de este tipo, aunque todas sus variables sean numéricas se puede optar por discretizarlas o simplemente aplicar las reglas y obtener los diferentes rangos de las variables predictoras.
A continuación se procede a la carga de los datos, el dataset fue previamente descargado al entorno local y colocado en el directorio data, que cuelga del directorio principal del proyecto. También se obtendrá una vista panorámica de los datos, principalmente para comprobar el formato de estos, su tamaño y su dimensionalidad.
library(plotly)
# Loading the data file.
diabetes.raw <- read.csv("./data/diabetes.csv", header = TRUE)
Para tener una primera idea de como está conformado el dataset es necesario realizar una primera exploración de sus características generales.
# Getting the structure of the data set.
str(diabetes.raw)
## 'data.frame': 768 obs. of 9 variables:
## $ Pregnancies : int 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : int 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : int 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : int 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : int 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : int 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : int 1 0 1 0 1 0 1 0 1 1 ...
# Looking basics info of the attributes.
summary(diabetes.raw)
## Pregnancies Glucose BloodPressure SkinThickness
## Min. : 0.000 Min. : 0.0 Min. : 0.00 Min. : 0.00
## 1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 62.00 1st Qu.: 0.00
## Median : 3.000 Median :117.0 Median : 72.00 Median :23.00
## Mean : 3.845 Mean :120.9 Mean : 69.11 Mean :20.54
## 3rd Qu.: 6.000 3rd Qu.:140.2 3rd Qu.: 80.00 3rd Qu.:32.00
## Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00
## Insulin BMI DiabetesPedigreeFunction Age
## Min. : 0.0 Min. : 0.00 Min. :0.0780 Min. :21.00
## 1st Qu.: 0.0 1st Qu.:27.30 1st Qu.:0.2437 1st Qu.:24.00
## Median : 30.5 Median :32.00 Median :0.3725 Median :29.00
## Mean : 79.8 Mean :31.99 Mean :0.4719 Mean :33.24
## 3rd Qu.:127.2 3rd Qu.:36.60 3rd Qu.:0.6262 3rd Qu.:41.00
## Max. :846.0 Max. :67.10 Max. :2.4200 Max. :81.00
## Outcome
## Min. :0.000
## 1st Qu.:0.000
## Median :0.000
## Mean :0.349
## 3rd Qu.:1.000
## Max. :1.000
# Looking a panoramic view of the data.
head(diabetes.raw)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 0 33.6
## 2 1 85 66 29 0 26.6
## 3 8 183 64 0 0 23.3
## 4 1 89 66 23 94 28.1
## 5 0 137 40 35 168 43.1
## 6 5 116 74 0 0 25.6
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 5 2.288 33 1
## 6 0.201 30 0
Antes de proceder a manipular los datos se realiza un análisis visual de las variables predictoras del conjunto de datos. Como los datos están en formato numérico se utilizan histogramas para representarlos, el objetivo de este análisis es complementar el análisis realizado anteriormente en el que se observaron las principales características de los datos, tales como: formato, dimensionalidad y tipo.
La función visual_analy() se encarga de crear la representación visual de todas las variables a la vez, esta función será utilizada en varios puntos del proyecto para comprobar el estado de los datos.
visual_analy <- function(df){
# Create a empty list for plots.
plot_lys <- list()
# Get the names of all varibales in the raw data set.
names <- colnames(df[1:8])
# Fill a plots's list with plot_ly histograms.
for (i in 1:length(names)){
fig <- plot_ly(x = df[,i],
type = "histogram",
histnorm = "count",
name = names[i])
plot_lys[[i]] <- fig
}
# Create a subplot with each plot in the list.
repres <- subplot(plot_lys, nrows = 2)
repres
}
Inicialmente se realiza el análisis visual a los datos originales.
# Visual Analysis
visual_analy(diabetes.raw)
De este primer análisis se obtienen las siguientes conclusiones.
En este apartado se realizarán todas las modificaciones necesarias para mejorar la calidad de los datos, poniendo especial interés en el tratamiento a los valores ausentes y a los valores extremos.
El primer paso que se realiza es crear una copia de los datos originales, en esta copia se almacenan los datos durante todo el proceso de preparación y limpieza.
# Create a copy for cleaned data.
diabetes.cl <- diabetes.raw
De la gráfica mostrada en el apartado de Visualicación Inicial del Datset se intuye que existen algunos valores que pueden ser clasificados como outliers, como por ejemplo el valor de una paciente que muestra que estuvo embarazada 17 veces, o los valores del índice de masa corporal cercano a cero, estos valores a simple vista parecen extraños con respecto a los valores de su muestra. Por este motivo será necesario realizar un análisis de cada una de las variables que componen el dataset para detectar los valores extremos y tratarlos según convenga.
Para facilitar el trabajo en la búsqueda de outliers se crea la función check_values(), que recibirá como parámetro la variable que se desea analizar y devuelve un gráfico Box Plot y los valores de los posibles outlilers.
En cada caja de gráfico se muestra la siguiente información estadística de cada variable:
Además de esta información, el gráfico se divide en cuatro partes más, mostrando una caja con información referente a los siguientes tipos.
check_values <- function(feature){
fig <- plot_ly(type = 'box')
fig <- fig %>% add_boxplot(y = feature,
jitter = 0.3,
pointpos = -1.8,
boxpoints = 'all',
marker = list(color = 'rgb(7,40,89)'),
line = list(color = 'rgb(7,40,89)'),
name = "All Points")
fig <- fig %>% add_boxplot(y = feature,
name = "Only Whiskers",
boxpoints = FALSE,
marker = list(color = 'rgb(9,56,125)'),
line = list(color = 'rgb(9,56,125)'))
fig <- fig %>% add_boxplot(y = feature,
name = "Suspected Outlier",
boxpoints = 'suspectedoutliers',
marker = list(color = 'rgb(8,81,156)',
outliercolor = 'rgba(219, 64, 82, 0.6)',
line = list(outliercolor = 'rgba(219, 64, 82, 1.0)',
outlierwidth = 2)),
line = list(color = 'rgb(8,81,156)'))
fig <- fig %>% add_boxplot(y = feature,
name = "Whiskers and Outliers",
boxpoints = 'outliers',
marker = list(color = 'rgb(107,174,214)'),
line = list(color = 'rgb(107,174,214)'))
fig <- fig %>% layout(title = "Box Plot Indentifying Outliers")
# Get the posibles outliers.
outliers <- boxplot.stats(feature)$out
return(list(fig=fig,out=outliers))
}
Se efectúa la búsqueda de outliers en cada una de las variables del dataset. En los casos en los que se considera necesario se eliminan los registros del conjunto de datos.
# Get the results of the analysis.
values = check_values(diabetes.cl$Pregnancies)
# Boxplot figure.
values$fig
# Outliers.
values$out
## [1] 15 17 14 14
# Deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$Pregnancies %in% values$out),]
# Check the result of deleting process.
values <- check_values(diabetes.cl$Pregnancies)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$Glucose)
# Boxplot figure.
values$fig
# Outliers.
values$out
## [1] 0 0 0 0 0
En los valores de la glucosa no se detectan outliers.
# Get the results of the analysis.
values = check_values(diabetes.cl$BloodPressure)
# Boxplot figure.
values$fig
# Outliers.
values$out
## [1] 0 0 30 110 0 0 0 0 108 122 30 0 110 0 0 0 0 0 0
## [20] 0 0 0 0 108 0 0 0 0 0 0 0 0 0 0 110 0 24 0
## [39] 0 0 0 114 0 0 0
# Deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$BloodPressure %in% values$out),]
# Check the result of deleting process.
values <-check_values(diabetes.cl$BloodPressure)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$SkinThickness)
# Boxplot figure
values$fig
# Outliers
values$out
## [1] 99
# deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$SkinThickness %in% values$out),]
# Check the result of deleting process.
values <- check_values(diabetes.cl$SkinThickness)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$Insulin)
# Boxplot figure
values$fig
# Outliers
values$out
## [1] 543 846 342 495 485 495 478 744 370 680 402 375 545 360 465 415 579 474 328
## [20] 480 326 330 600 440 540 480 335 387 392 510
# deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$Insulin %in% values$out),]
# Check the result of deleting process.
values <- check_values(diabetes.cl$Insulin)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$BMI)
# Boxplot figure
values$fig
# Outliers
values$out
## [1] 0.0 53.2 0.0 50.0 52.9 0.0 59.4 57.3 0.0
# deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$BMI %in% values$out),]
# Check the result of deleting process.
values <- check_values(diabetes.cl$BMI)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$DiabetesPedigreeFunction)
# Boxplot figure
values$fig
# Outliers
values$out
## [1] 2.288 1.441 1.390 1.893 1.781 1.222 1.400 1.189 1.321 1.224 1.318 1.213
## [13] 1.353 1.224 1.391 1.476 1.268 1.600 1.191 1.251 1.699 1.258 1.282 1.698
## [25] 1.461 1.292 1.394 1.174 1.182
# deleting the outliers.
diabetes.cl <- diabetes.cl[-which(diabetes.cl$DiabetesPedigreeFunction %in% values$out),]
# Check the result of deleting process.
values <- check_values(diabetes.cl$DiabetesPedigreeFunction)
values$fig
# Get the results of the analysis.
values = check_values(diabetes.cl$Age)
# Boxplot figure
values$fig
# Outliers
values$out
## [1] 69 65 66 65 67 81 67 66 67 66 70 68 66
En el caso de la edad aunque estadísticamente estos valores sean outliers no se eliminarán porque son edades razonables, es decir, que incluso la edad con valor de 81 años es una edad común entre la población.
De manera similar a la ocurrida con los outliers en el apartado Visualicación Inicial del Datset también se han identificado algunos valores que pueden corresponder a missing values. Más específicamente se han detectado una gran cantidad de 0 “ceros” en los datos, principalmente en las variables " Insulin" y " SkinThickness".
Al analizar un poco más en profundidad estas variables se detecta que sus valores no pueden ser cero, es imposible que los valores de glucosa de una persona sean cero, o igualmente que los niveles de insulina después de dos horas sea cero, con lo cual estos valores demuestran que corresponden a valores nulos. Por ende se procede a establecerlos como NaN, para facilitar su posterior tratamiento, destacar que no se sustituyen en todas las variables, ya que en algunas si es posible que el cero sea una medida válida, como por ejemplo en la variable " Pregnancies", que indica la cantidad de embarazos de las mujeres.
# Replace 0s with Nan.
nan_names <- c('Glucose','BloodPressure','SkinThickness','Insulin','BMI')
diabetes.cl[nan_names][diabetes.cl[nan_names] == 0] <- NaN
# Looking a panoramic view of the data.
head(diabetes.cl)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 NaN 33.6
## 2 1 85 66 29 NaN 26.6
## 3 8 183 64 NaN NaN 23.3
## 4 1 89 66 23 94 28.1
## 6 5 116 74 NaN NaN 25.6
## 7 3 78 50 32 88 31.0
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 6 0.201 30 0
## 7 0.248 26 1
Para seleccionar la manera más adecuada de tratar los valores ausentes es necesario realizar algunas pruebas, estas pruebas permiten conocer algunas características de estos valores, y al final del proceso los resultados de estas pruebas serán definitorios a la hora de elegir un método para tratar los missing values.
Inicialmente se cuentan los missing values por cada variable. Y se calcula el porcentaje que representan estos valores del total de entradas en el conjunto de datos.
# Amount of NaN values per each column.
colSums(is.na(diabetes.cl))
## Pregnancies Glucose BloodPressure
## 0 5 0
## SkinThickness Insulin BMI
## 182 314 0
## DiabetesPedigreeFunction Age Outcome
## 0 0 0
# Percercent of null values.
perc <- (sum(is.na(diabetes.cl$SkinThickness)))/nrow(diabetes.cl)*100
paste("Porciento de valores nulos en SkinThickness:", round(perc,2))
## [1] "Porciento de valores nulos en SkinThickness: 28"
perc <- (sum(is.na(diabetes.cl$Insulin)))/nrow(diabetes.cl)*100
paste("Porciento de valores nulos en Insulin:", round(perc,2))
## [1] "Porciento de valores nulos en Insulin: 48.31"
Como se puede apreciar en el anterior análisis un gran porciento de los datos contiene missin values, llegando a alcanzar el 48% en el caso de la variable Insulin, con lo cual el método para tratar estos datos debe ser elegido con cuidado. Será necesario continuar con el análisis de estos valores para tomar una decisión.
Partiendo de la base de que la variable Insulin es la que mayor porcentaje de missing valeus tiene, se calcula la proporción de los valores nulos de SkinThickness que también son valores nulos en Insulin.
# Get the matches in null values in both variables.
sum((is.na(diabetes.cl$SkinThickness)) & (is.na(diabetes.cl$Insulin)))
## [1] 182
El análisis anterior muestra que todos lo valores nulos de la variable SkinThickness también lo son en la variable Insulin, lo que demuestra que estos registros presentan serios problemas. Puede ser porque no fueron tomados por los doctores en el momento de hacer el estudio,o porque solo tomaban las muestras de las personas que pensaban que podían presentar la enfermedad.
Se evalúan las correlaciones de esta variable con la variable objetivo para ver que influencia tienen estos valores en la predicción.
# Get the correlation with target variable.
cor.test(diabetes.cl$Insulin, diabetes.raw$Outcome[diabetes.cl$Insulin])
##
## Pearson's product-moment correlation
##
## data: diabetes.cl$Insulin and diabetes.raw$Outcome[diabetes.cl$Insulin]
## t = 2.8276, df = 334, p-value = 0.004973
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.04666807 0.25570989
## sample estimates:
## cor
## 0.1528989
cor.test(diabetes.cl$SkinThickness, diabetes.raw$Outcome[diabetes.cl$SkinThickness])
##
## Pearson's product-moment correlation
##
## data: diabetes.cl$SkinThickness and diabetes.raw$Outcome[diabetes.cl$SkinThickness]
## t = -5.0493, df = 466, p-value = 6.373e-07
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.3119581 -0.1400051
## sample estimates:
## cor
## -0.2277565
Como se puede apreciar los valores de las correlaciones no son muy altos, con lo cuál estas variables no tienen una gran influencia en la variable objetivo.
Este hecho ayuda a tomar la decisión de sustituir todos los registros que contengan valores nulos, Se seleccionará el valor de la mediana de cada una de las variables que contengan valores ausentes para la sustitución.
Inicialmente se había analizado la opción de eliminar estos datos, pero esto trae consigo una pérdida de datos muy significativa para el modelo, ya que hubiese sido necesario eliminar casi el 50% de los datos, y se perdería la capacidad predictora de las demás variables. Es cierto que al sustituir estos valores por los de la mediana se introducen muchos datos al modelo, y esto puede acarrear un sesgo en la información, pero es una solución mejor que la de eliminarlos. Además los datos de las medianas en estos casos son valores que pueden ser considerados normales, ya que corresponden a valores de personas sanas en estas pruebas.
Tal y como se había expresado anteriormente, se procede a remplazar los missing vlaues por el valor de la mediana de cada un de sus variables.
# Replace the values with the median value.
for(i in 1:ncol(diabetes.cl)){
diabetes.cl[is.na(diabetes.cl[,i]), i] <- median(diabetes.cl[,i], na.rm = TRUE)
}
# Getting the structure of the data set.
str(diabetes.cl)
## 'data.frame': 650 obs. of 9 variables:
## $ Pregnancies : int 6 1 8 1 5 3 4 10 5 0 ...
## $ Glucose : num 148 85 183 89 116 78 110 168 166 118 ...
## $ BloodPressure : int 72 66 64 66 74 50 92 74 72 84 ...
## $ SkinThickness : num 35 29 29 23 29 32 29 29 19 47 ...
## $ Insulin : num 116 116 116 94 116 ...
## $ BMI : num 33.6 26.6 23.3 28.1 25.6 31 37.6 38 25.8 45.8 ...
## $ DiabetesPedigreeFunction: num 0.627 0.351 0.672 0.167 0.201 0.248 0.191 0.537 0.587 0.551 ...
## $ Age : int 50 31 32 21 30 26 30 34 51 31 ...
## $ Outcome : int 1 0 1 0 0 1 0 1 1 1 ...
# Looking basics info of the attributes.
summary(diabetes.cl)
## Pregnancies Glucose BloodPressure SkinThickness
## Min. : 0.000 Min. : 44.0 Min. : 38.00 Min. : 7.0
## 1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:25.0
## Median : 3.000 Median :114.0 Median : 72.00 Median :29.0
## Mean : 3.802 Mean :119.1 Mean : 72.09 Mean :28.7
## 3rd Qu.: 6.000 3rd Qu.:136.8 3rd Qu.: 80.00 3rd Qu.:32.0
## Max. :13.000 Max. :198.0 Max. :106.00 Max. :60.0
## Insulin BMI DiabetesPedigreeFunction Age
## Min. : 15.0 Min. :18.20 Min. :0.0780 Min. :21.0
## 1st Qu.:114.2 1st Qu.:27.30 1st Qu.:0.2395 1st Qu.:24.0
## Median :115.5 Median :32.00 Median :0.3595 Median :29.0
## Mean :122.3 Mean :31.98 Mean :0.4252 Mean :33.1
## 3rd Qu.:120.0 3rd Qu.:36.00 3rd Qu.:0.5830 3rd Qu.:40.0
## Max. :325.0 Max. :49.70 Max. :1.1620 Max. :81.0
## Outcome
## Min. :0.0000
## 1st Qu.:0.0000
## Median :0.0000
## Mean :0.3123
## 3rd Qu.:1.0000
## Max. :1.0000
# Looking a panoramic view of the data.
head(diabetes.cl)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 115.5 33.6
## 2 1 85 66 29 115.5 26.6
## 3 8 183 64 29 115.5 23.3
## 4 1 89 66 23 94.0 28.1
## 6 5 116 74 29 115.5 25.6
## 7 3 78 50 32 88.0 31.0
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 50 1
## 2 0.351 31 0
## 3 0.672 32 1
## 4 0.167 21 0
## 6 0.201 30 0
## 7 0.248 26 1
# Percent of losted data.
perc <- 100 - nrow(diabetes.cl)/nrow(diabetes.raw)*100
cat("Porciento de datos perdiddos:", round(perc,2))
## Porciento de datos perdiddos: 15.36
Obtenemos una visión general de los datos una vez eliminados los outliers y sustituidos los missing values. Se puede apreciar que se ha prescindido de más de un 15% de los datos.
# Visual Analysis
visual_analy(diabetes.cl)
En la gráfica anterior se aprecia como han cambiado las disposiciones de las variables que tenían un número elevado de valores ausentes ( Insulin , SkinThickness ), presentando ahora todos los valores que se han sustituido por la mediana.
En este apartado se procede a analizar las relaciones de cada una de las variables predictoras con la variable target, intentando encontrar patrones o comportamientos que puedan aportar cualquier información sobre el conjunto de datos. También se convertirán los valores de la variable Outcome a categóricos, esta tarea facilitará el posterior uso de la variable target a la hora de analizar las diferentes relaciones entre esta variable y las predictoras.
Para trabajar en el análisis exploratorio se crea una copia de los datos limpios.
# Create a copy of the cleaned dataset.
diabetes.exp <- diabetes.cl
diabetes.exp$Outcome[diabetes.exp$Outcome == 1] <- "Healthy"
diabetes.exp$Outcome[diabetes.exp$Outcome == 0] <- "Diabetic"
library(ggplot2)
# Discretizing age variable
diabetes.exp["Age"] <- cut(diabetes.exp$Age, breaks=c(-Inf,23,35,55,+Inf),
labels=c("young","adults","mid-age","old"))
# Looking a panoramic view of the data.
head(diabetes.exp)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1 6 148 72 35 115.5 33.6
## 2 1 85 66 29 115.5 26.6
## 3 8 183 64 29 115.5 23.3
## 4 1 89 66 23 94.0 28.1
## 6 5 116 74 29 115.5 25.6
## 7 3 78 50 32 88.0 31.0
## DiabetesPedigreeFunction Age Outcome
## 1 0.627 mid-age Healthy
## 2 0.351 adults Diabetic
## 3 0.672 adults Healthy
## 4 0.167 young Diabetic
## 6 0.201 adults Diabetic
## 7 0.248 adults Healthy
x <- table(diabetes.exp$Age,diabetes.exp$Outcome)/nrow(diabetes.exp) * 100
x
##
## Diabetic Healthy
## young 20.615385 2.615385
## adults 29.846154 11.846154
## mid-age 14.000000 14.769231
## old 4.307692 2.000000
# Looking for appearances.
ggplot(data = diabetes.exp,aes(x=Age,fill=Outcome))+geom_bar(binwidth = 3,position="dodge")+ylab("Count") + xlab("Age")
Tal y como se puede apreciar en la gráfica, la mayoría de los casos tanto negativos como positivos se encuentran en las personas adultas y de mediana edad.
# Looking for appearances.
ggplot(data = diabetes.exp,aes(x=Pregnancies,fill=Outcome))+geom_bar(binwidth = 3,position="dodge")+ylab("Count") + xlab("Pregnancies")
En la gráfica se aprecia que el las mujeres embarazadas tienen una mayor probabilidad de sufrir diabetes.
En este apartado se intentará obtener alguna información de la variable Glucose con respecto a la Outcome. .
# Looking for appearances.
fig <- plot_ly(x = diabetes.exp$Glucose,
color = diabetes.exp$Outcome,
colors = c("#F35E48", "#7CA6D7"),
type = "histogram",
xbins = data.frame(
start=min(diabetes.raw$Glucose)-1,
end=max(diabetes.raw$Glucose)+1,
size=5
))
fig
En esta gráfica se puede apreciar que la mayoría de casos con resultado positivo al examen de Diabetes tienen valores entre 90 y 120.
# Looking for appearances.
fig <- plot_ly(x = diabetes.exp$SkinThickness,
color = diabetes.exp$Outcome,
colors = c("#F35E48", "#7CA6D7"),
type = "histogram",
xbins = data.frame(
start=min(diabetes.exp$SkinThickness)-1,
end=max(diabetes.exp$SkinThickness)+1,
size=3
))
fig
# Looking for appearances.
fig <- plot_ly(x = diabetes.exp$Insulin,
color = diabetes.exp$Outcome,
colors = c("#F35E48", "#7CA6D7"),
type = "histogram",
xbins = data.frame(
start=min(diabetes.exp$Insulin)-1,
end=max(diabetes.exp$Insulin)+1,
size=15
))
fig
# Looking for appearances.
fig <- plot_ly(x = diabetes.exp$BMI,
color = diabetes.exp$Outcome,
colors = c("#F35E48", "#7CA6D7"),
type = "histogram",
xbins = data.frame(
start=min(diabetes.exp$BMI)-1,
end=max(diabetes.exp$BMI)+1,
size=3
))
fig
Este análisis no muestra relevancias con respecto a la salida, ambas clases (diabéticos y no diabéticos).
# Looking for appearances.
fig <- plot_ly(x = diabetes.exp$DiabetesPedigreeFunction,
color = diabetes.exp$Outcome,
colors = c("#F35E48", "#7CA6D7"),
type = "histogram",
xbins = data.frame(
start=min(diabetes.exp$DiabetesPedigreeFunction)-1,
end=max(diabetes.exp$DiabetesPedigreeFunction)+1,
size=0.5
))
fig
En este apartado de pretende buscar las correlaciones entre cada una de las variables del dataset, tomándolas de dos a dos y construyendo la matriz de correlaciones. También se intentará identificar las diferentes distribuciones de estas gracias a sus histogramas.
Para facilitar esta tarea se usa una poderosa herramienta que permite ver ambas características a la vez, los pairs plots. En la diagonal principal de se muestran las distribuciones de las variables, en la mitad inferior se observan los gráficos de densidad y en la parte superior de la figura la matriz de correlación (se utiliza la correlación de pearson).
library(psych)
pairs.panels(diabetes.exp,
method = "pearson", # correlation method
hist.col = "#00AFBB",
density = TRUE, # show density plots
ellipses = TRUE # show correlation ellipses
)
De los resultados obtenidos en el análisis exploratorio se pueden obtener las siguientes conclusiones:
En este apartado se aplicará un proceso de SVD al dataset con el que se ha trabajado hasta el momento. El SVD es una de las más poderosas técnicas del Álgebra lineal utilizada para la reducción del tamaño y la dimensionalidad de los datos. Puede ser utilizada en la compresión de imágenes, en la reducción del ruido en los datos o en la elaboración de sistemas de recomendación, entre otras muchas aplicaciones. Las principales empresas tecnológicas de momento utilizan de alguna manera estas técnica, por ejemplo Netflix lo utiliza para sus sistema de recomendación de contenido multimedia, Amazon para recomendar sus productos y Facebook al sus sistemas de reconocimiento facial.
Esta técnica utiliza como base otra de los más utilizados procedimientos para la reducción de la dimensionalidad, PCA ( Principal Component Analysis) (Wikipedia contributors, 2020), solo que en lugar de buscar las características principales no correlacionadas en las variables originales, el SVD busca en ambas dimensiones, analizando tanto los registros como las características. A continuación de ofrece una reseña de los fundamentes matemáticos detrás de esta técnica y se aplica al conjunto de datos en cuestión.
La técnica de SVD consiste en reducir la matriz original \(A\)* con rank \(r\), en una matriz más pequeña con rank \(k\). Este método es aplicable tanto a matrices cuadradas como a rectangulares, y los valores de las matrices pueden ser reales o complejos, la única restricción es que tienen que ser numéricos ya que utilizan las correlaciones para realizar el análisis.
La reducción se realiza factorizando la matriz \(A\) de la forma \(m\)x\(n\) en la multiplicación de tres matrices, de la forma \(A= UΣV^{T}\).
Donde:
\(m\): La cantidad de variables de la base de datos.
\(n\): La cantidad de entradas en la base de datos.
\(U\): Matriz de tamaño \(m\)x\(p\) , cuyas columnas son el resultado de calcular los eigenvectors de la matriz de correlaciones \(AA^{T}\).
Esta matriz posee las siguientes características:
\(Σ\): Matriz diagonal que contiene los valores singulares de \(r\), que son calculados como las raíces cuadradas de los eigeivalues distintos de cero de \(A^{T}A\) y \(AA^{T}\). Estos valores se pueden identificar como los niveles de la varianza (\(\sigma\)) de cada uno de los singular vectors.
Esta matriz tiene algunas características que le otorgan una gran importancia:
\(V^{T}\): Es una matriz de tamaño \(p\)x\(n\) , cuyas columnas son el resultado de calcular los eigenvectors de la matriz de correlaciones \(A^{T}A\).
Esta matriz posee las siguientes características:
Ej: El vector \(v_2^{T}\) que corresponde a la segunda fila de la matriz \(V\), es una mezcla entre la segunda columna \(u_2\) de la matriz \(U\) y la segunda columna de la matriz \(A\), y como siempre escalado por el valor de la variable \(\sigma_2\).
Una vez conocidos los principios matemáticos que rigen este procedimiento se puede proceder a aplicar la técnica SVD para reducir la dimensionalidad de los conjuntos de datos. Partiendo de la base de las características de las matrices \(U\), \(Σ\) y \(V^{T}\), que demuestran que los valores de estas matrices están ordenados por importancia, se seleccionarán solo los más importantes para que conformen el modelo final.
Con el objetivo de facilitar el análisis se propone un ejemplo con un conjunto de datos pequeño que contiene información de los ratings de películas para algunas personas.
# Create a matrix.
A = as.matrix(data.frame(c(4,7,-1,8), c(-5,-2,4,2), c(-1,3,-3,6)))
# Set the columns and rows names.
colnames(A) <- c("RamboII","Rocky IV","Harry Potter")
rownames(A) <- c("Bart","Michael","Tim","Sophie")
A
## RamboII Rocky IV Harry Potter
## Bart 4 -5 -1
## Michael 7 -2 3
## Tim -1 4 -3
## Sophie 8 2 6
En este ejemplo se utilizará la siguiente nomenclatura para identificar las diferentes matrices:
Para obtener estas matrices solamente es necesario invocar la función svd() pasando como parámetro la matriz A. Una de las características más importante de este método es que el tamaño de la matriz \(S\) determinará el tamaño de las otras dos matrices, y este tamaño viene determinado por el rank de la matriz, que define la cantidad de filas y columnas linearmente independientes.
A continuación se calcula el valor el rank \(r\) y se aplica el método.
library(Matrix)
# Get the rank of A matrix.
r <- rankMatrix(A)
r[1]
## [1] 3
# Appliying SVD method to matrix A.
A.svd <- svd(A)
# Set the values to each matrix.
U <- A.svd$u
U
## [,1] [,2] [,3]
## [1,] -0.2816569 0.7303849 -0.42412326
## [2,] -0.5912537 0.1463017 -0.18371213
## [3,] 0.2247823 -0.4040717 -0.88586638
## [4,] -0.7214994 -0.5309048 0.04012567
S <- A.svd$d
S
## [1] 13.161210 6.999892 3.432793
Vt <- A.svd$v
Vt
## [,1] [,2] [,3]
## [1,] -0.8557101 0.01464091 -0.5172483
## [2,] 0.1555269 -0.94610374 -0.2840759
## [3,] -0.4935297 -0.32353262 0.8073135
En este punto del proceso ya se cuenta con la descomposición de la matriz original \(A\), y las matrices que la componen ya han sido calculadas, ahora es necesario realizar la reducción de la dimensionalidad, y para ello se utilizan los valores de los componentes de las matrices y el valor de \(r\).
Primero es necesario analizar como está compuesta la matriz \(A\).
\(A = \sigma_1u_1v_1^{T} + \sigma_2u_2v_2^{T} + ... \sigma_m*u_m*v_m^{T}\)
Es conocido que al estar ordenados por importancia los componentes, la cantidad de información relevante irá en descenso a medida que aumentan los valores de \(r\), por este motivo es necesario seleccionar un valor de \(r\) en el cual se corte la sumatoria anteriormente definida. De esta manera se reduce la dimensionalidad y se mantiene la mayoría de la información relevante del modelo.
En la siguiente figura la parte señalada en color rojo será la correspondiente a valores de \(r\) que no son seleccionados para formar parte del modelo. Este nuevo valor de \(r\) se denotará como \(k\).
De modo que el modelo quedaría con la siguiente esxtructura.
\(A= U_{mxk}Σ_{kxk}V_{kxn}^{T}\)
Para elegir el número adecuado vectores para que conformen el modelo y en consecuencia el valor de \(k\) es necesario fijarse en la energía que contiene el modelo. A continuación se analizan varios aspectos para lograr una correcta elección de \(k\).
La energía es igual a la suma de los cuadrados de los valores de la matriz \(Σ\).
\(e= \sigma_1^{2} +\sigma_2^{2}+...\sigma_m^{2}\)
El correcto valor de \(r\) debe ser el que contenga al menos el 90% de la energía, para determinarlo se eliminan los valores de \(Σ\) con mayor índice y se comprueba la energía del resto.
# Energy.
e_total <- 0
# Total energy.
for (i in S){
e_total <- e_total + i^2
}
e_total
## [1] 234
# Get the 90%.
e.90 <- 0.9*e_total
e.90
## [1] 210.6
e <- 0
# Energy with only 2 sigma values.
for (i in S[1:2]){
e <- e + i^2
}
# Condition.
print(e > e.90)
## [1] TRUE
Como resultado del análisis anterior se puede seleccionar un valor de \(k = 2\), ya que con estos valores singulares el modelo contiene más del 90% de la energía. En este caso hay muy pocas oportunidades de elegir un valor adecuado, a medida que aumentan estos valores de \(r\) esta técnica aporta mayores beneficios. Otra manera muy útil de obtener este valor es gracias a la visualización de la energía acumulada y los valores de los singular values, pero en este caso como hay tan pocos valores de \(\sigma\) no se pueden apreciar bien las magnitudes.
En este apartado se aplicará el método de SVD al conjunto de datos Pima Indians Diabetes Database. Se seguirán los mismos pasos que los realizados con la matriz de prueba y se razonarán los resultados obtenidos. En este caso como se cuenta con todos los datos es conveniente aplicar la reducción al conjunto entero de los datos, para luego si fuese necesario dividirlo en testing y training o realizar alguna otra modificación, los datos estén comprimidos bajo los mismos parámetros.
El objetivo de este procedimiento es obtener las 3 matrices de menor tamaño, almacenarlas para luego poder reconstruir los datos a partir de ellas.
Primeramente antes de aplicar el método se escalan los datos usando el escalado a z, con media 0 y varianza 1, para que los datos estén todos en el mismo orden. Posteriormente se ordenan los elementos del dataset, este paso es opcional, pero a su vez sirve para comparar los resultados obtenidos luego de aplicar el proceso de SVD.
# Scale the data.
diabetes.cl[1:8] <- scale(diabetes.cl[1:8])
# Order the elements.
h <- hclust(dist(diabetes.cl[,c(1:8)]))
diabetes.ordered <- diabetes.cl[h$order, ]
head(diabetes.ordered)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 238 -1.17155684 2.0539008 1.583027871 -0.20282199 -0.13372769 1.895122
## 733 -0.55519752 1.8825667 1.406244413 0.99024854 -0.04495023 1.957664
## 682 -1.17155684 1.4713648 0.345543666 0.87094149 -0.13372769 2.755086
## 747 -0.86337718 0.9573624 1.936594786 1.46747676 -0.13372769 2.708179
## 379 0.06116181 1.2657638 0.257151937 0.03579212 -0.13372769 2.551822
## 236 0.06116181 1.7797662 -0.008023249 0.03579212 -0.13372769 1.816943
## DiabetesPedigreeFunction Age Outcome
## 238 1.0698738 -0.86264029 1
## 733 0.9057604 -0.77721735 1
## 682 -0.2512386 -0.60637146 1
## 747 -0.2758556 -0.52094852 1
## 379 -0.7681956 -0.09383382 1
## 236 0.2205873 -0.60637146 1
Una vez el se cuenta con el dataset ordenado se separa la variable objetivo del mismo y se almacena en la variable. Luego se calcula el rango de la matriz para saber tener una idea del formato del conjunto de datos, este valor indicará cuantas columnas y filas son linealmente independientes. Y para finalizar este paso se aplica el SVD a la matriz \(A\), que como resultado devuelve las tres matrices con las mismas nomenclaturas utilizadas anteriormente.
# Split the target variable.
Outcome = diabetes.ordered$Outcome
# Create the original matrix A.
A <- as.matrix(diabetes.ordered)
# Get the rank of A matrix.
r <- rankMatrix(A[,c(1:8)])
paste("rank:", r[1])
## [1] "rank: 8"
# Applying SVD method to matrix A.
A.svd <- svd(A[,c(1:8)])
# Set the values to each matrix.
U <- A.svd$u
S <- A.svd$d
Vt <- A.svd$v
Al igual que con el ejemplo es necesario conocer cual es el número optimo de \(k\) para reducir la dimensionalidad de nuestros datos. Del análisis del paso anterior se obtiene que el valor de rank es igual a 8, y de entre estos 8 valores de \(r\) se necesitan obtener los \(k\) valores que aporten el 90% de la información. Este valor determinará la dimensionalidad de las amtrices que se almacenarán al finalizar el proceso.
# Energy.
e_total <- 0
# Total energy.
for (i in S){
e_total <- e_total + i^2
}
paste("Total Energy:" ,e_total)
## [1] "Total Energy: 5192"
# Get the 90%.
e.90 <- 0.9*e_total
paste("90% of Energy:" ,e.90)
## [1] "90% of Energy: 4672.8"
e <- 0
# Energy with only 2 sigma values.
for (i in S[1:7]){
e <- e + i^2
}
# Condition
paste("e > 90%:" ,e > e.90)
## [1] "e > 90%: TRUE"
El número óptimo de \(k\) es 7, ya que con los 7 primeros valores de \(\sigma\) se explican más del 90% de los datos. Otro mecanismo muy utilizado para encontrar el número óptimo de \(k\) es graficando su energía.
# Total value of S square.
all_sing_sq <- sum(S^2)
# Percent vales per element of S.
perc_vec <- NULL
for (i in 1:length(S)) {
perc_vec[i] <- sum(S[1:i]^2) / all_sing_sq
}
# Plot the energy.
plot(perc_vec, pch=20, col = "blue", cex = 1.5, xlab='Singular Value', ylab='% of Sum of Squares of Singular Values', main = "Choosing k for Dimensionality Reduction")
lines(x = c(0,100), y = c(.90, .90))
Se puede apreciar en la gráfica que la línea que indica el 90% está justo pisando el valor de \(\sigma = 6\), pero se selecciona 7 como valor óptimo de \(k\) para asegurarnos que se cumple con la condición.
# Set the k value.
k <- 7
Con este valor de k ya se pueden conformar las matrices \(U\), \(Σ\) y \(V^{T}\) definitivas, las cuales tendrán como prefijo a su nomenclatura normal los caracteres “diab_”.
# Set the final values.
diab_U <- U[,1:k]
diab_S <- S[1:k]
diab_Vt <- Vt[,1:k]
Estas matrices junto con la variable Outcome serán los valores que se almacenarán para posteriormente reconstruir el modelo.
Como paso final y a modo de comprobación, se procede a reconstruir la matriz \(A\) a partir de sus descomposiciones almacenadas, se establece el valor e \(k = r\) y se comprueba que se obtiene la matriz original.
diabetes.svd <- U %*% diag(S) %*% t(Vt)
# Check the equality.
head(diabetes.svd)
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] -1.17155684 2.0539008 1.583027871 -0.20282199 -0.13372769 1.895122
## [2,] -0.55519752 1.8825667 1.406244413 0.99024854 -0.04495023 1.957664
## [3,] -1.17155684 1.4713648 0.345543666 0.87094149 -0.13372769 2.755086
## [4,] -0.86337718 0.9573624 1.936594786 1.46747676 -0.13372769 2.708179
## [5,] 0.06116181 1.2657638 0.257151937 0.03579212 -0.13372769 2.551822
## [6,] 0.06116181 1.7797662 -0.008023249 0.03579212 -0.13372769 1.816943
## [,7] [,8]
## [1,] 1.0698738 -0.86264029
## [2,] 0.9057604 -0.77721735
## [3,] -0.2512386 -0.60637146
## [4,] -0.2758556 -0.52094852
## [5,] -0.7681956 -0.09383382
## [6,] 0.2205873 -0.60637146
head(diabetes.ordered)
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 238 -1.17155684 2.0539008 1.583027871 -0.20282199 -0.13372769 1.895122
## 733 -0.55519752 1.8825667 1.406244413 0.99024854 -0.04495023 1.957664
## 682 -1.17155684 1.4713648 0.345543666 0.87094149 -0.13372769 2.755086
## 747 -0.86337718 0.9573624 1.936594786 1.46747676 -0.13372769 2.708179
## 379 0.06116181 1.2657638 0.257151937 0.03579212 -0.13372769 2.551822
## 236 0.06116181 1.7797662 -0.008023249 0.03579212 -0.13372769 1.816943
## DiabetesPedigreeFunction Age Outcome
## 238 1.0698738 -0.86264029 1
## 733 0.9057604 -0.77721735 1
## 682 -0.2512386 -0.60637146 1
## 747 -0.2758556 -0.52094852 1
## 379 -0.7681956 -0.09383382 1
## 236 0.2205873 -0.60637146 1
En esta práctica se ha recorrido la primera parte de un proyecto de minería de datos, correspondiente a los procesos de limpieza y preparación de los datos. Se ha seleccionado un dataset de un repositorio de datos abiertos y se ha acondicionado para cumplir con las necesidades de la práctica. Se ha realizado el tratamiento tanto de los valores extremos como de los valores ausentes. Igualmente se ha realizado un análisis exploratorio de los datos para intentar observas sus principales comportamientos respecto a la variable objetivo.
Todos estos pasos sirvieron de premisa para al final de la práctica aplicar el método de Descomposición en Valores Singulares, método que supuso todo un reto porque fue investigado de manera autodidacta. Con la aplicación del mismo se consiguió afianzar los conocimientos adquiridos durante la investigación, demostrando la importancia de este para proyectos reales de minería de datos. Es una lástima que los datos no pudiesen ser reducidos un poco más, debido a que sus variables presentaban muy poca correlación y eran pocas, pero aún así pude asimilar el potencial de este método. De manera análoga tuve que consultar información acerca de PCA, que aunque no se haya implementado en esta práctica, he fijado los conocimientos básicos de este método.
Material de la asignatura Minería de Datos. Canal de Youtube de Steven Brunton Profesor de la Universidad de Washington
Wikipedia contributors. (2020). PCA — Wikipedia, the free encyclopedia. https://es.wikipedia.org/wiki/An%C3%A1lisis_de_componentes_principales